home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / osdep / pipe < prev    next >
Text File  |  1996-07-11  |  13KB  |  466 lines

  1. /*======================================================================
  2.     pipe
  3.     
  4.     Initiate I/O to and from a process.  These functions are similar to 
  5.     popen and pclose, but both an incoming stream and an output file are 
  6.     provided.
  7.    
  8.  ====*/
  9.  
  10. #ifndef    STDIN_FILENO
  11. #define    STDIN_FILENO    0
  12. #endif
  13. #ifndef    STDOUT_FILENO
  14. #define    STDOUT_FILENO    1
  15. #endif
  16. #ifndef    STDERR_FILENO
  17. #define    STDERR_FILENO    2
  18. #endif
  19.  
  20.  
  21. /*
  22.  * Defs to help fish child's exit status out of wait(2)
  23.  */
  24. #ifdef    HAVE_WAIT_UNION
  25. #define WaitType    union wait
  26. #ifndef    WIFEXITED
  27. #define    WIFEXITED(X)    (!(X).w_termsig)    /* child exit by choice */
  28. #endif
  29. #ifndef    WEXITSTATUS
  30. #define    WEXITSTATUS(X)    (X).w_retcode        /* childs chosen exit value */
  31. #endif
  32. #else
  33. #define    WaitType    int
  34. #ifndef    WIFEXITED
  35. #define    WIFEXITED(X)    (!((X) & 0xff))        /* low bits tell how it died */
  36. #endif
  37. #ifndef    WEXITSTATUS
  38. #define    WEXITSTATUS(X)    (((X) >> 8) & 0xff)    /* high bits tell exit value */
  39. #endif
  40. #endif
  41.  
  42.  
  43. /*
  44.  * Global's to helpsignal handler tell us child's status has changed...
  45.  */
  46. short    child_signalled;
  47. short    child_jump = 0;
  48. jmp_buf child_state;
  49.  
  50.  
  51. /*
  52.  * Internal Protos
  53.  */
  54. void pipe_error_cleanup PROTO((PIPE_S **, char *, char *, char *));
  55. void zot_pipe PROTO((PIPE_S **));
  56.  
  57.  
  58.  
  59.  
  60. /*----------------------------------------------------------------------
  61.      Spawn a child process and optionally connect read/write pipes to it
  62.  
  63.   Args: command -- string to hand the shell
  64.     outfile -- address of pointer containing file to receive output
  65.     errfile -- address of pointer containing file to receive error output
  66.     mode -- mode for type of shell, signal protection etc...
  67.   Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
  68.  
  69.   The outfile is either NULL, a pointer to a NULL value, or a pointer
  70.   to the requested name for the output file.  In the pointer-to-NULL case
  71.   the caller doesn't care about the name, but wants to see the pipe's
  72.   results so we make one up.  It's up to the caller to make sure the
  73.   free storage containing the name is cleaned up.
  74.  
  75.   Mode bits serve several purposes.
  76.     PIPE_WRITE tells us we need to open a pipe to write the child's
  77.     stdin.
  78.     PIPE_READ tells us we need to open a pipe to read from the child's
  79.     stdout/stderr.  *NOTE*  Having neither of the above set means 
  80.     we're not setting up any pipes, just forking the child and exec'ing
  81.     the command.  Also, this takes precedence over any named outfile.
  82.     PIPE_STDERR means we're to tie the childs stderr to the same place
  83.     stdout is going.  *NOTE* This only makes sense then if PIPE_READ
  84.     or an outfile is provided.  Also, this takes precedence over any
  85.     named errfile.
  86.     PIPE_PROT means to protect the child from the usual nasty signals
  87.     that might cause premature death.  Otherwise, the default signals are
  88.     set so the child can deal with the nasty signals in its own way.     
  89.     PIPE_NOSHELL means we're to exec the command without the aid of
  90.     a system shell.  *NOTE* This negates the affect of PIPE_USER.
  91.     PIPE_USER means we're to try executing the command in the user's
  92.     shell.  Right now we only look in the environment, but that may get
  93.     more sophisticated later.
  94.     PIPE_RESET means we reset the terminal mode to what it was before
  95.     we started pine and then exec the command.
  96.  ----*/
  97. PIPE_S *
  98. open_system_pipe(command, outfile, errfile, mode)
  99.     char  *command;
  100.     char **outfile, **errfile;
  101.     int    mode;
  102. {
  103.     PIPE_S *syspipe = NULL;
  104.     char    shellpath[32], *shell;
  105.     int     p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
  106.  
  107.     dprint(5, (debugfile, "Opening pipe: \"%s\" (%s%s%s%s%s%s)\n", command,
  108.            (mode & PIPE_WRITE)   ? "W":"", (mode & PIPE_READ)  ? "R":"",
  109.            (mode & PIPE_NOSHELL) ? "N":"", (mode & PIPE_PROT)  ? "P":"",
  110.            (mode & PIPE_USER)    ? "U":"", (mode & PIPE_RESET) ? "T":""));
  111.  
  112.     syspipe = (PIPE_S *)fs_get(sizeof(PIPE_S));
  113.     memset(syspipe, 0, sizeof(PIPE_S));
  114.  
  115.     /*
  116.      * If we're not using the shell's command parsing smarts, build
  117.      * argv by hand...
  118.      */
  119.     if(mode & PIPE_NOSHELL){
  120.     char   **ap, *p;
  121.     size_t   n;
  122.  
  123.     /* parse the arguments into argv */
  124.     for(p = command; *p && isspace((unsigned char)(*p)); p++)
  125.       ;                    /* swallow leading ws */
  126.  
  127.     if(*p){
  128.         syspipe->args = cpystr(p);
  129.     }
  130.     else{
  131.         pipe_error_cleanup(&syspipe, "<null>", "execute",
  132.                    "No command name found");
  133.         return(NULL);
  134.     }
  135.  
  136.     for(p = syspipe->args, n = 2; *p; p++)    /* count the args */
  137.       if(isspace((unsigned char)(*p))
  138.          && *(p+1) && !isspace((unsigned char)(*(p+1))))
  139.         n++;
  140.  
  141.     syspipe->argv = ap = (char **)fs_get(n * sizeof(char *));
  142.     memset(syspipe->argv, 0, n * sizeof(char *));
  143.  
  144.     for(p = syspipe->args; *p; ){        /* collect args */
  145.         while(*p && isspace((unsigned char)(*p)))
  146.           *p++ = '\0';
  147.  
  148.         *ap++ = (*p) ? p : NULL;
  149.         while(*p && !isspace((unsigned char)(*p)))
  150.           p++;
  151.     }
  152.  
  153.     /* make sure argv[0] exists in $PATH */
  154.     if(can_access_in_path(getenv("PATH"), syspipe->argv[0],
  155.                   EXECUTE_ACCESS) < 0){
  156.         pipe_error_cleanup(&syspipe, syspipe->argv[0], "access",
  157.                    error_description(errno));
  158.         return(NULL);
  159.     }
  160.     }
  161.  
  162.     /* fill in any output filenames */
  163.     if(!(mode & PIPE_READ)){
  164.     if(outfile && !*outfile)
  165.       *outfile = temp_nam(NULL, "pine_p");    /* asked for, but not named? */
  166.  
  167.     if(errfile && !*errfile)
  168.       *errfile = temp_nam(NULL, "pine_p");    /* ditto */
  169.     }
  170.  
  171.     /* create pipes */
  172.     if(mode & (PIPE_WRITE | PIPE_READ)){
  173.     if(mode & PIPE_WRITE){
  174.         pipe(p);                /* alloc pipe to write child */
  175.         oparentd = p[STDOUT_FILENO];
  176.         ichildd  = p[STDIN_FILENO];
  177.     }
  178.  
  179.     if(mode & PIPE_READ){
  180.         pipe(p);                /* alloc pipe to read child */
  181.         iparentd = p[STDIN_FILENO];
  182.         ochildd  = p[STDOUT_FILENO];
  183.     }
  184.     }
  185.     else if(!(mode & PIPE_SILENT)){
  186.     flush_status_messages(0);        /* just clean up display */
  187.     ClearScreen();
  188.     fflush(stdout);
  189.     }
  190.  
  191.     if((syspipe->mode = mode) & PIPE_RESET)
  192.       Raw(0);
  193.  
  194. #ifdef    SIGCHLD
  195.     /*
  196.      * Prepare for demise of child.  Use SIGCHLD if it's available so
  197.      * we can do useful things, like keep the IMAP stream alive, while
  198.      * we're waiting on the child.
  199.      */
  200.     child_signalled = child_jump = 0;
  201. #endif
  202.  
  203.     if((syspipe->pid = vfork()) == 0){
  204.      /* reset child's handlers in requested fashion... */
  205.     (void)signal(SIGINT,  (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
  206.     (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
  207.     (void)signal(SIGHUP,  (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
  208. #ifdef    SIGCHLD
  209.     (void) signal(SIGCHLD,  SIG_DFL);
  210. #endif
  211.  
  212.     /* if parent isn't reading, and we have a filename to write */
  213.     if(!(mode & PIPE_READ) && outfile){    /* connect output to file */
  214.         int output = creat(*outfile, 0600);
  215.         dup2(output, STDOUT_FILENO);
  216.         if(mode & PIPE_STDERR)
  217.           dup2(output, STDERR_FILENO);
  218.         else if(errfile)
  219.           dup2(creat(*errfile, 0600), STDERR_FILENO);
  220.     }
  221.  
  222.     if(mode & PIPE_WRITE){            /* connect process input */
  223.         close(oparentd);
  224.         dup2(ichildd, STDIN_FILENO);    /* tie stdin to pipe */
  225.         close(ichildd);
  226.     }
  227.  
  228.     if(mode & PIPE_READ){            /* connect process output */
  229.         close(iparentd);
  230.         dup2(ochildd, STDOUT_FILENO);    /* tie std{out,err} to pipe */
  231.         if(mode & PIPE_STDERR)
  232.           dup2(ochildd, STDERR_FILENO);
  233.         else if(errfile)
  234.           dup2(creat(*errfile, 0600), STDERR_FILENO);
  235.  
  236.         close(ochildd);
  237.     }
  238.  
  239.     if(mode & PIPE_NOSHELL){
  240.         execvp(syspipe->argv[0], syspipe->argv);
  241.     }
  242.     else{
  243.         if(mode & PIPE_USER){
  244.         char *env, *sh;
  245.         if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
  246.             shell = sh + 1;
  247.             strcpy(shellpath, env);
  248.         }
  249.         else{
  250.             shell = "csh";
  251.             strcpy(shellpath, "/bin/csh");
  252.         }
  253.         }
  254.         else{
  255.         shell = "sh";
  256.         strcpy(shellpath, "/bin/sh");
  257.         }
  258.  
  259.         execl(shellpath, shell, command ? "-c" : 0, command, 0);
  260.     }
  261.  
  262.     fprintf(stderr, "Can't exec %s\nReason: %s",
  263.         command, error_description(errno));
  264.     _exit(-1);
  265.     }
  266.  
  267.     if(syspipe->pid > 0){
  268.     syspipe->isig = signal(SIGINT,  SIG_IGN); /* Reset handlers to make */
  269.     syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to  */
  270.     syspipe->hsig = signal(SIGHUP,  SIG_IGN); /* a premature end...     */
  271.  
  272.     if(mode & PIPE_WRITE){
  273.         close(ichildd);
  274.         if(mode & PIPE_DESC)
  275.           syspipe->out.d = oparentd;
  276.         else
  277.           syspipe->out.f = fdopen(oparentd, "w");
  278.     }
  279.  
  280.     if(mode & PIPE_READ){
  281.         close(ochildd);
  282.         if(mode & PIPE_DESC)
  283.           syspipe->in.d = iparentd;
  284.         else
  285.           syspipe->in.f = fdopen(iparentd, "r");
  286.     }
  287.  
  288.     dprint(5, (debugfile, "PID: %d, COMMAND: %s\n",syspipe->pid,command));
  289.     }
  290.     else{
  291.     if(mode & (PIPE_WRITE | PIPE_READ)){
  292.         if(mode & PIPE_WRITE){
  293.         close(oparentd);
  294.         close(ichildd);
  295.         }
  296.  
  297.         if(mode & PIPE_READ){
  298.         close(iparentd);
  299.         close(ochildd);
  300.         }
  301.     }
  302.     else if(!(mode & PIPE_SILENT)){
  303.         ClearScreen();
  304.         ps_global->mangled_screen = 1;
  305.     }
  306.  
  307.     if(mode & PIPE_RESET)
  308.       Raw(1);
  309.  
  310. #ifdef    SIGCHLD
  311.     (void) signal(SIGCHLD,  SIG_DFL);
  312. #endif
  313.     if(outfile)
  314.       fs_give((void **) outfile);
  315.  
  316.     pipe_error_cleanup(&syspipe, command, "fork",error_description(errno));
  317.     }
  318.  
  319.     return(syspipe);
  320. }
  321.  
  322.  
  323.  
  324. /*----------------------------------------------------------------------
  325.     Write appropriate error messages and cleanup after pipe error
  326.  
  327.   Args: syspipe -- address of pointer to struct to clean up
  328.     cmd -- command we were trying to exec
  329.     op -- operation leading up to the exec
  330.     res -- result of that operation
  331.  
  332.  ----*/
  333. void
  334. pipe_error_cleanup(syspipe, cmd, op, res)
  335.     PIPE_S **syspipe;
  336.     char    *cmd, *op, *res;
  337. {
  338.     q_status_message3(SM_ORDER, 3, 3, "Pipe can't %s \"%.20s\": %s",
  339.               op, cmd, res);
  340.     dprint(1, (debugfile, "* * PIPE CAN'T %s(%s): %s\n", op, cmd, res));
  341.     zot_pipe(syspipe);
  342. }
  343.  
  344.  
  345.  
  346. /*----------------------------------------------------------------------
  347.     Free resources associated with the given pipe struct
  348.  
  349.   Args: syspipe -- address of pointer to struct to clean up
  350.  
  351.  ----*/
  352. void
  353. zot_pipe(syspipe)
  354.     PIPE_S **syspipe;
  355. {
  356.     if((*syspipe)->args)
  357.       fs_give((void **) &(*syspipe)->args);
  358.  
  359.     if((*syspipe)->argv)
  360.       fs_give((void **) &(*syspipe)->argv);
  361.  
  362.     if((*syspipe)->tmp)
  363.       fs_give((void **) &(*syspipe)->tmp);
  364.  
  365.     fs_give((void **)syspipe);
  366. }
  367.  
  368.  
  369.  
  370. /*----------------------------------------------------------------------
  371.     Close pipe previously allocated and wait for child's death
  372.  
  373.   Args: syspipe -- address of pointer to struct returned by open_system_pipe
  374.   Returns: returns exit status of child or -1 if invalid syspipe
  375.  ----*/
  376. int
  377. close_system_pipe(syspipe)
  378.     PIPE_S **syspipe;
  379. {
  380.     WaitType stat;
  381.     int         status;
  382.  
  383.     if(!(syspipe && *syspipe))
  384.       return(-1);
  385.  
  386.     if(((*syspipe)->mode) & PIPE_WRITE){
  387.     if(((*syspipe)->mode) & PIPE_DESC){
  388.         if((*syspipe)->out.d >= 0)
  389.           close((*syspipe)->out.d);
  390.     }
  391.     else if((*syspipe)->out.f)
  392.       fclose((*syspipe)->out.f);
  393.     }
  394.  
  395.     if(((*syspipe)->mode) & PIPE_READ){
  396.     if(((*syspipe)->mode) & PIPE_DESC){
  397.         if((*syspipe)->in.d >= 0)
  398.           close((*syspipe)->in.d);
  399.     }
  400.     else if((*syspipe)->in.f)
  401.       fclose((*syspipe)->in.f);
  402.     }
  403.  
  404. #ifdef    SIGCHLD
  405.     {
  406.     SigType (*alarm_sig)();
  407.     int    old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
  408.  
  409.     /*
  410.      * remember the current SIGALRM handler, and make sure it's
  411.      * installed when we're finished just in case the longjmp
  412.      * out of the SIGCHLD handler caused sleep() to lose it.
  413.      * Don't pay any attention to that man behind the curtain.
  414.      */
  415.     alarm_sig = signal(SIGALRM, SIG_IGN);
  416.     (void) signal(SIGALRM, alarm_sig);
  417.     F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
  418.     ps_global->noshow_timeout = 1;
  419.     while(!child_signalled){
  420.         new_mail(0, 2, 0);        /* wake up and prod server */
  421.  
  422.         if(!child_signalled){
  423.         if(setjmp(child_state) == 0){
  424.             child_jump = 1;    /* prepare to wake up */
  425.             sleep(600);        /* give it 5mins to happend */
  426.         }
  427.         else
  428.           pine_sigunblock(SIGCHLD);
  429.         }
  430.  
  431.         child_jump = 0;
  432.     }
  433.  
  434.     ps_global->noshow_timeout = 0;
  435.     F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
  436.     (void) signal(SIGALRM, alarm_sig);
  437.     }
  438. #endif
  439.  
  440.     /*
  441.      * Call c-client's pid reaper to wait() on the demise of our child,
  442.      * then fish out its exit status...
  443.      */
  444.     grim_pid_reap_status((*syspipe)->pid, 0, &stat);
  445.     status = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
  446.  
  447.     /*
  448.      * restore original handlers...
  449.      */
  450.     (void)signal(SIGINT,  (*syspipe)->isig);
  451.     (void)signal(SIGHUP,  (*syspipe)->hsig);
  452.     (void)signal(SIGQUIT, (*syspipe)->qsig);
  453.  
  454.     if((*syspipe)->mode & PIPE_RESET)        /* restore our tty modes */
  455.       Raw(1);
  456.  
  457.     if(!((*syspipe)->mode & (PIPE_WRITE | PIPE_READ | PIPE_SILENT))){
  458.     ClearScreen();                /* No I/O to forked child */
  459.     ps_global->mangled_screen = 1;
  460.     }
  461.  
  462.     zot_pipe(syspipe);
  463.  
  464.     return(status);
  465. }
  466.